package sample4.provider.dml;

import java.util.ArrayList;
import java.util.List;

/**
 * <pre>
 * ...
 * SqliteWhereBuilder where = new SqliteWhereBuilder();
 * where.whereAndEquals("_id", 10);
 * 
 * SqliteUpdateBuilder sql = new SqliteUpdateBuilder(uri);
 * sql.addContentValue("key", 0);
 * sql.whereAndWhere(where);
 * int update = sql.update(getContentResolver());
 * ...
 * </pre>
 */
public class SqliteWhereBuilder {
    private StringBuilder mWhere = new StringBuilder();
    private List<String> mArgs = new ArrayList<String>();
    private List<SqliteWhereBuilder> mAndOtherWheres = new ArrayList<SqliteWhereBuilder>();
    private List<SqliteWhereBuilder> mOrOtherWheres = new ArrayList<SqliteWhereBuilder>();

    /**
     * コンストラクタ
     */
    public SqliteWhereBuilder() {
    }

    private void whereAnd(boolean and) {
        if (and) {
            if (mWhere.length() != 0) {
                mWhere.append(" AND ");
            }
        } else {
            if (mWhere.length() != 0) {
                mWhere.append(" OR ");
            }
        }
    }

    private static String[] stringArray(Number... args) {
        String[] strs = new String[args.length];
        for (int i = 0; i < strs.length; i++) {
            strs[i] = args[i].toString();
        }
        return strs;
    }

    private SqliteWhereBuilder whereEquals(boolean and, String column, String arg) {
        whereAnd(and);
        mWhere.append(column).append("=?");
        mArgs.add(arg);
        return this;
    }

    // ? column != ?
    private SqliteWhereBuilder whereNotEquals(boolean and, String column, String arg) {
        whereAnd(and);
        mWhere.append(column).append("!=?");
        mArgs.add(arg);
        return this;
    }

    // ? column IN(?,?,?...)
    private SqliteWhereBuilder whereIn(boolean and, String column, String... args) {
        whereAnd(and);
        StringBuilder inArgs = new StringBuilder();
        for (String arg : args) {
            if (inArgs.length() != 0) {
                inArgs.append(",");
            }
            inArgs.append(arg);
        }
        mWhere.append(column).append(" IN(").append(inArgs).append(")");
        return this;
    }

    // ? column IN(?,?,?...)
    private SqliteWhereBuilder whereIn(boolean and, String column, List<String> args) {
        whereAnd(and);
        StringBuilder inArgs = new StringBuilder();
        for (String arg : args) {
            if (inArgs.length() != 0) {
                inArgs.append(",");
            }
            inArgs.append(arg);
        }
        mWhere.append(column).append(" IN(").append(inArgs).append(")");
        return this;
    }

    // ? column NOT IN(?,?,?...)
    private SqliteWhereBuilder whereNotIn(boolean and, String column, String... args) {
        whereAnd(and);
        StringBuilder inArgs = new StringBuilder();
        for (String arg : args) {
            if (inArgs.length() != 0) {
                inArgs.append(",");
            }
            inArgs.append(arg);
        }
        mWhere.append(column).append(" NOT IN(").append(inArgs).append(")");
        return this;
    }

    // ? column LIKE ?
    private SqliteWhereBuilder whereLike(boolean and, String column, String arg) {
        whereAnd(and);
        mWhere.append(column).append(" LIKE '").append(arg).append("')");
        return this;
    }

    // ? column NOT LIKE ?
    private SqliteWhereBuilder whereNotLike(boolean and, String column, String arg) {
        whereAnd(and);
        mWhere.append(column).append(" NOT LIKE '").append(arg).append("')");
        return this;
    }

    // ? column IS NULL
    private SqliteWhereBuilder whereIsNull(boolean and, String column) {
        whereAnd(and);
        mWhere.append(column).append(" IS NULL ");
        return this;
    }

    // ? column IS NOT NULL
    private SqliteWhereBuilder whereIsNotNull(boolean and, String column) {
        whereAnd(and);
        mWhere.append(column).append(" IS NOT NULL ");
        return this;
    }

    /**
     * And otherWhere And otherWhere And otherWhere ...
     * 
     * @param otherWheres
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndWhere(SqliteWhereBuilder... otherWheres) {
        for (SqliteWhereBuilder where : otherWheres) {
            mAndOtherWheres.add(where);
        }
        return this;
    }

    /**
     * OR otherWhere OR otherWhere OR otherWhere ...
     * 
     * @param otherWheres
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrWhere(SqliteWhereBuilder... otherWheres) {
        for (SqliteWhereBuilder where : otherWheres) {
            mOrOtherWheres.add(where);
        }
        return this;
    }

    /**
     * AND column = ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndEquals(String column, String arg) {
        return whereEquals(true, column, arg);
    }

    /**
     * AND column != ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndNotEquals(String column, String arg) {
        return whereNotEquals(true, column, arg);
    }

    /**
     * AND column IN(?,?,?...)
     * 
     * @param column
     * @param args
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndIn(String column, String... args) {
        return whereIn(true, column, args);
    }

    /**
     * AND column IN(?,?,?...)
     * 
     * @param column
     * @param args
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndIn(String column, List<String> args) {
        return whereIn(true, column, args);
    }

    /**
     * AND column NOT IN(?,?,?...)
     * 
     * @param column
     * @param args
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndNotIn(String column, String... args) {
        return whereNotIn(true, column, args);
    }

    /**
     * AND column LIKE ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndLike(String column, String arg) {
        return whereLike(true, column, arg);
    }

    /**
     * AND column NOT LIKE ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndNotLike(String column, String arg) {
        return whereNotLike(true, column, arg);
    }

    /**
     * AND column IS NULL
     * 
     * @param column
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndIsNull(String column) {
        return whereIsNull(true, column);
    }

    /**
     * AND column IS NOT NULL
     * 
     * @param column
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndIsNotNull(String column) {
        return whereIsNotNull(true, column);
    }

    /**
     * OR column = ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrEquals(String column, String arg) {
        return whereEquals(false, column, arg);
    }

    /**
     * OR column != ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrNotEquals(String column, String arg) {
        return whereNotEquals(false, column, arg);
    }

    /**
     * OR column IN(?,?,?...)
     * 
     * @param column
     * @param args
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrIn(String column, String... args) {
        return whereIn(false, column, args);
    }

    /**
     * OR column IN(?,?,?...)
     * 
     * @param column
     * @param args
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrIn(String column, List<String> args) {
        return whereIn(false, column, args);
    }

    /**
     * OR column NOT IN(?,?,?...)
     * 
     * @param column
     * @param args
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrNotIn(String column, String... args) {
        return whereNotIn(false, column, args);
    }

    /**
     * OR column LIKE ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrLike(String column, String arg) {
        return whereLike(false, column, arg);
    }

    /**
     * OR column NOT LIKE ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrNotLike(String column, String arg) {
        return whereNotLike(false, column, arg);
    }

    /**
     * OR column IS NULL
     * 
     * @param column
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrIsNull(String column) {
        return whereIsNull(false, column);
    }

    /**
     * OR column IS NOT NULL
     * 
     * @param column
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrIsNotNull(String column) {
        return whereIsNotNull(false, column);
    }

    /**
     * AND column = ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndEquals(String column, Number arg) {
        return whereAndEquals(column, arg.toString());
    }

    /**
     * AND column <> ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndNotEquals(String column, Number arg) {
        whereAnd(true);
        mWhere.append(column).append("<>?");
        mArgs.add(arg.toString());
        return this;
    }

    /**
     * AND column IN(?,?,?...)
     * 
     * @param column
     * @param args
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndIn(String column, Number... args) {
        return whereAndIn(column, stringArray(args));
    }

    /**
     * AND column NOT IN(?,?,?...)
     * 
     * @param column
     * @param args
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndNotIn(String column, Number... args) {
        return whereAndNotIn(column, stringArray(args));
    }

    /**
     * AND column < ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndLessThan(String column, Number arg) {
        whereAnd(true);
        mWhere.append(column).append("<?");
        mArgs.add(arg.toString());
        return this;
    }

    /**
     * AND column > ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndGreaterThan(String column, Number arg) {
        whereAnd(true);
        mWhere.append(column).append(">?");
        mArgs.add(arg.toString());
        return this;
    }

    /**
     * AND column <= ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndLessThanOrEquals(String column, Number arg) {
        whereAnd(true);
        mWhere.append(column).append("<=?");
        mArgs.add(arg.toString());
        return this;
    }

    /**
     * AND column >= ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndGreaterThanOrEquals(String column, Number arg) {
        whereAnd(true);
        mWhere.append(column).append(">=?");
        mArgs.add(arg.toString());
        return this;
    }

    /**
     * AND lessThan <= column AND column <= greaterThan
     * 
     * @param column
     * @param lessThan
     * @param greaterThan
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndBetween(String column, Number lessThan, Number greaterThan) {
        whereAndLessThanOrEquals(column, greaterThan);
        whereAndGreaterThanOrEquals(column, lessThan);
        return this;
    }

    /**
     * AND column < lessThan AND greaterThan < column
     * 
     * @param column
     * @param lessThan
     * @param greaterThan
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereAndNotBetween(String column, Number lessThan, Number greaterThan) {
        whereAndLessThan(column, lessThan);
        whereAndGreaterThan(column, greaterThan);
        return this;
    }

    /**
     * OR column = ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrEquals(String column, Number arg) {
        return whereOrEquals(column, arg.toString());
    }

    /**
     * OR column <> ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrNotEquals(String column, Number arg) {
        whereAnd(false);
        mWhere.append(column).append("<>?");
        mArgs.add(arg.toString());
        return this;
    }

    /**
     * OR column IN(?,?,?...)
     * 
     * @param column
     * @param args
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrIn(String column, Number... args) {
        return whereOrIn(column, stringArray(args));
    }

    /**
     * OR column NOT IN(?,?,?...)
     * 
     * @param column
     * @param args
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrNotIn(String column, Number... args) {
        return whereOrNotIn(column, stringArray(args));
    }

    /**
     * OR column < ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrLessThan(String column, Number arg) {
        whereAnd(false);
        mWhere.append(column).append("<?");
        mArgs.add(arg.toString());
        return this;
    }

    /**
     * OR column > ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrGreaterThan(String column, Number arg) {
        whereAnd(false);
        mWhere.append(column).append(">?");
        mArgs.add(arg.toString());
        return this;
    }

    /**
     * OR column <= ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrLessThanOrEquals(String column, Number arg) {
        whereAnd(false);
        mWhere.append(column).append("<=?");
        mArgs.add(arg.toString());
        return this;
    }

    /**
     * OR column >= ?
     * 
     * @param column
     * @param arg
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrGreaterThanOrEquals(String column, Number arg) {
        whereAnd(false);
        mWhere.append(column).append(">=?");
        mArgs.add(arg.toString());
        return this;
    }

    /**
     * OR lessThan <= column AND column <= greaterThan
     * 
     * @param column
     * @param lessThan
     * @param greaterThan
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrBetween(String column, Number lessThan, Number greaterThan) {
        whereOrLessThanOrEquals(column, greaterThan);
        whereOrGreaterThanOrEquals(column, lessThan);
        return this;
    }

    /**
     * OR column < lessThan AND greaterThan < column
     * 
     * @param column
     * @param lessThan
     * @param greaterThan
     * @return SqliteWhereBuilder
     */
    public SqliteWhereBuilder whereOrNotBetween(String column, Number lessThan, Number greaterThan) {
        whereOrLessThan(column, lessThan);
        whereOrGreaterThan(column, greaterThan);
        return this;
    }

    /**
     * Whereパートを取得する
     * 
     * @return Whereパート
     */
    public String getWhere() {
        StringBuilder where = new StringBuilder();
        if (0 < mWhere.length()) {
            where.append(mWhere.toString());
        }
        //
        for (int i = 0; i < mAndOtherWheres.size(); i++) {
            if (0 == i && 0 < where.length()) {
                where.append(" AND ");
            }
            where.append(mAndOtherWheres.get(i).getWhere());
        }
        //
        for (int i = 0; i < mAndOtherWheres.size(); i++) {
            if (0 == i && 0 < where.length()) {
                where.append(" OR ");
            }
            where.append(mOrOtherWheres.get(i).getWhere());
        }

        return where.toString();
    }

    /**
     * Whereパートの引数配列を取得する
     * 
     * @return Whereパートの引数配列
     */
    public String[] getWhereArgs() {
        for (SqliteWhereBuilder otherWhere : mAndOtherWheres) {
            for (String otherWhereArg : otherWhere.getWhereArgs()) {
                mArgs.add(otherWhereArg);
            }
        }
        for (SqliteWhereBuilder otherWhere : mOrOtherWheres) {
            for (String otherWhereArg : otherWhere.getWhereArgs()) {
                mArgs.add(otherWhereArg);
            }
        }
        return mArgs.toArray(new String[] {});
    }
}
